/**
 * Copyright (C) 2008 Christopher Allen Ogden
 *
 *     This file is part of Odysi.
 *
 *  Odysi is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Odysi is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Odysi.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sstream>
#include <stdexcept>
#include "clientLua.h"
#include "stateMachine.h"
#include "musicManager.h"
#include "postService.h"
#include "luaAux.h"
#include "map.h"
#include "character.h"

namespace {
    /**
     * Set the current state.
     */
    static int setState(lua_State *L)
    {
        double s = luaL_checknumber(L, 1);
        StateMachine::GS() = s;
        return 0;
    }

    /**
     * Play a music file.
     */
    static int fadeInMusic(lua_State *L)
    {
        double time = luaL_checknumber(L, 2);
        std::stringstream ss;
        ss << PKGDATADIR << "/" << lua_tostring(L, 1);
        MusicManager::GS().fadeIn(ss.str(), static_cast<int>(time));
        return 0;
    }

    /**
     * Send login packet.
     */
    static int sendLoginPacket(lua_State *L)
    {
        PostService *post = reinterpret_cast<PostService *>(Lua::getRegisteredPointer(L, LUA_CR_POST_SERVICE));
        Packet packet(genPacket::login(lua_tostring(L, 1), lua_tostring(L, 2)));
        post->send(packet);
        return 0;
    }

    /**
     * Add a character.
     */
    static int addCharacter(lua_State *L)
    {
        Map *map = reinterpret_cast<Map *>(Lua::getRegisteredPointer(L, LUA_CR_MAP));

        Character *chr = new Character;
        chr->setX(luaL_checknumber(L, 1));
        chr->setY(luaL_checknumber(L, 2));
        chr->setZ(luaL_checknumber(L, 3));
        chr->setMobID(luaL_checknumber(L, 4));
        chr->setID(luaL_checknumber(L, 5));
        chr->setMax(map->getWidth() - 1, map->getHeight() - 1);
        chr->setRed(luaL_checknumber(L, 6));
        chr->setGreen(luaL_checknumber(L, 7));
        chr->setBlue(luaL_checknumber(L, 8));
        chr->setAlpha(luaL_checknumber(L, 9));

        map->addObject(chr);
        chr->setSpeed(CHARACTER_SPEED);
        return 0;
    }

    /**
     * Move the player.
     */
    static int sendMoveRequestPacket(lua_State *L)
    {
        PostService *post = reinterpret_cast<PostService *>(Lua::getRegisteredPointer(L, LUA_CR_POST_SERVICE));
        Packet packet(genPacket::moveRequest(lua_tointeger(L, 1)));
        post->send(packet);
        return 0;
    }

    static int moveObject(lua_State *L)
    {
        Map *map = reinterpret_cast<Map *>(Lua::getRegisteredPointer(L, LUA_CR_MAP));
        Mobile *obj = dynamic_cast<Mobile *>(map->getDynamicObject(luaL_checknumber(L, 1)));
        if (obj) {
            obj->move(luaL_checknumber(L, 2), luaL_checknumber(L, 3), luaL_checknumber(L, 4));
        }
        return 0;
    }

    static int localCall(lua_State *L)
    {
        boost::shared_ptr<Packet> pkt = Lua::createRemoteCall(L);
        Lua::processRemoteCall(L, *pkt, -1);
    }

    static int remoteCall(lua_State *L)
    {
        boost::shared_ptr<Packet> pkt = Lua::createRemoteCall(L);
        PostService *post = reinterpret_cast<PostService *>(Lua::getRegisteredPointer(L, LUA_CR_POST_SERVICE));
        post->send(*pkt);
    }

    /**
     * Stores a list of lua functions.
     */
    static const struct luaL_Reg odysic [] = {
        {"setState", setState},
        {"fadeInMusic", fadeInMusic},
        {"sendLoginPacket", sendLoginPacket},
        {"addCharacter", addCharacter},
        {"sendMoveRequestPacket", sendMoveRequestPacket},
        {"moveObject", moveObject},
        {"localCall", localCall},
        {"remoteCall", remoteCall},
        {NULL, NULL}
    };
}

void call_tick(lua_State *L)
{
    lua_getglobal(L, "tick");
    if (lua_pcall(L, 0, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}

void call_gotLoginOKPacket(lua_State *L)
{
    lua_getglobal(L, "gotLoginOKPacket");
    if (lua_pcall(L, 0, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}

void call_gotLoginFailPacket(lua_State *L)
{
    lua_getglobal(L, "gotLoginFailPacket");
    if (lua_pcall(L, 0, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}

void call_gotAddCharacterPacket(lua_State *L, const genPacket::AddCharacterPacket *ac)
{
    lua_getglobal(L, "gotAddCharacterPacket");
    lua_newtable(L);
    lua_pushstring(L, "x");
    lua_pushnumber(L, ac->x);
    lua_settable(L, -3);
    lua_pushstring(L, "y");
    lua_pushnumber(L, ac->y);
    lua_settable(L, -3);
    lua_pushstring(L, "z");
    lua_pushnumber(L, ac->z);
    lua_settable(L, -3);
    lua_pushstring(L, "mobID");
    lua_pushnumber(L, ac->mobID);
    lua_settable(L, -3);
    lua_pushstring(L, "id");
    lua_pushnumber(L, ac->id);
    lua_settable(L, -3);
    lua_pushstring(L, "red");
    lua_pushnumber(L, ac->red);
    lua_settable(L, -3);
    lua_pushstring(L, "green");
    lua_pushnumber(L, ac->green);
    lua_settable(L, -3);
    lua_pushstring(L, "blue");
    lua_pushnumber(L, ac->blue);
    lua_settable(L, -3);
    lua_pushstring(L, "alpha");
    lua_pushnumber(L, ac->alpha);
    lua_settable(L, -3);
    if (lua_pcall(L, 1, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}

void call_moveRequest(lua_State *L, int dir)
{
    lua_getglobal(L, "moveRequest");
    lua_pushnumber(L, dir);
    if (lua_pcall(L, 1, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}

int openOdysicLib (lua_State *L)
{
    luaL_register(L, "odysic", odysic);
    lua_pushinteger(L, StateMachine::GAME);
    lua_setglobal(L, "STATE_GAME");
    lua_pushinteger(L, StateMachine::LOGIN);
    lua_setglobal(L, "STATE_LOGIN");
    return 1;
}

void call_gotMoveObjectPacket(lua_State *L, const genPacket::MoveObjectPacket *mp)
{
    lua_getglobal(L, "gotMoveObjectPacket");
    lua_pushnumber(L, mp->id);
    lua_pushnumber(L, mp->xOff);
    lua_pushnumber(L, mp->yOff);
    lua_pushnumber(L, mp->zOff);
    if (lua_pcall(L, 4, 0, 0) != 0) {
        throw std::runtime_error(lua_tostring(L, -1));
    }
}
